; FANLED - FANcy Line EDitor for TurboDOS 1.4x
;
; RELEASE VERSION HISTORY
; 1.00	04/02/85 changed kill buffer logic to retain last kill buffer
;		 contents until ^K or ^X (rather than erasing at the
;		 beginning of each edit session).  This now allows
;		 use of the kill buffer as "storage" for a frequently
;		 used command.
; 1.10	04/05/85 Optimized code for size
; 1.11		 fixed quote problem
; 1.20  04/09/85 Made edit keys global
; 1.30  05/04/85 Fixed problem in ^Y routine
;
	name	('FANLED')
;
; Input:
;   HL points to a data structure:
;   <HL>   = CLBLEN (command line buffer length
;   <HL+1> = actual command line length
;   <HL+2> = first byte of null-terminated command line
; Output:
;   HL     = pointer to actual command line length
;   A      = actual command line length
;
.z80
;
; Externals used:
;
	extrn	dms,echo,conin,conout
;
; Global entry point:
;
	public	inpln
;
; Global patch point:
;
	public	forspc
;
; command character equates
;
beglin	equ	'A'-40h		; ^A - beginning of line
backch	equ	'B'-40h		; ^B - cursor backwards
delfor	equ	'D'-40h		; ^D - delete char forward
endlin	equ	'E'-40h		; ^E - end of line
forwch	equ	'F'-40h		; ^F - cursor forward
delbak	equ	'H'-40h		; ^H - delete char backward
kilfor	equ	'K'-40h		; ^K - kill forward
quote	equ	'Q'-40h		; ^Q - quote next char
recall	equ	'R'-40h		; ^R - recall previous command line
abort	equ	'U'-40h		; ^U - abort line
kilbak	equ	'X'-40h		; ^X - kill backward
yank	equ	'Y'-40h		; ^Y - yank (unkill)
delete	equ	7fh		; DEL- same as ^H
;
; other equates
;
cr	equ	0dh		; carriage return
bell	equ	7		; bell character
litc	equ	'^'		; caret (prefix for control char display)
;
; data storage areas
;
	dseg
;
forspc:	db	'L'-40h		; nondestructive forward space character
qflag:	db	0ffh		; quote next flag
clsave:	dw	0		; command line structure pointer save
clen:	db	0		; command line length
insy:	db	0		; insert yank flag
killen:	db	0		; length of kill buffer
kilbuf:	ds	162		; 162 byte kill buffer
oldlin:	ds	162		; old command line
;
; command table
;
cmdtbl:
fanbg::	db	beglin
	dw	cbegli
fanbk::	db	backch
	dw	cbackc
fanel::	db	endlin
	dw	cendli
fanfc::	db	forwch
	dw	cforwc
fandf::	db	delfor
	dw	cdelfo
fandb::	db	delbak
	dw	cdelba
fankf::	db	kilfor
	dw	ckilfo
fanqu::	db	quote
	dw	cquote
fanrc::	db	recall
	dw	crecal
fankb::	db	kilbak
	dw	ckilba
fanya::	db	yank
	dw	cyank
fanab::	db	abort
	dw	cabort
	db	delete
	dw	cdelba
	db	cr
	dw	exit
ncmds	equ	($-cmdtbl)/3		; number of commands
;
	cseg
;
; REGISTER USAGE:
; HL normally points to the current character in the command line
;    unless used otherwise
; DE normally points to the kill buffer or the old line buffer
; B  always holds the current byte count for the main command line
; C  always holds the current cursor position in the command line
; A  usually holds a byte received from the keyboard and/or echoed
;
; SUBROUTINES:
;
; GETCH - get character in A, preserve all regs
;
getch:	push	bc
	push	de
	push	hl
	call	conin
	jr	popr
;
; BKSPC - backspace
;
bkspc:	ld	a,delbak
;
; PUTCH - display character in A, preserve all regs
;
putch:	push	bc
	push	de
	push	hl
	ld	c,a
	push	af
	call	conout
	pop	af
popr:	pop	hl
	pop	de
	pop	bc
	ret
;
; ERROR - ring the bell
;
error:	ld	a,bell
	jr	putch
;
; DELLFT - delete left character with pointer movement
;          assume: HL points into command line, B holds
;	   current byte count
;
dellft:	dec	b		; decrement count
	dec	c		; decrement cursor pointer
	dec	hl		; decrement pointer
	ld	a,(hl)		; get byte
	cp	' '		; if control...
	jr	nc,delchl	; delete one char if no control
	inc	d		; increment control char counter
	call	delchl
;
; DELCHL - delete left character (bs,sp,bs)
;
delchl:	push	bc
	push	de
	push	hl
	call	dms
	db	8,' ',88h
	jr	popr
;
; GETCMD - A=character, determine if it's an edit command
;
getcmd:	push	hl
	push	bc
	cp	cr
	jr	z,iscr
	ld	hl,qflag
	bit	0,(hl)
	set	0,(hl)
	jr	z,getcmx
iscr:	ld	b,ncmds
	ld	hl,cmdtbl	; point to command table
getcml:	cp	(hl)
	jr	z,gotcmd	; got a command
	inc	hl
	inc	hl
	inc	hl
	djnz	getcml		; keep searching
	ld	b,a
	xor	a		; set zero flag
	ld	a,b
	jr	getcmx		; exit
;
gotcmd:	inc	hl
	ld	e,(hl)
	inc	hl
	ld	d,(hl)
	xor	a
	inc	a		; reset zero flag
getcmx:	pop	bc
	pop	hl
	ret
;
;
; MAIN PROGRAM ENTRY POINT
;
;
inpln:	xor	a		; zero byte count/cursor buffer
	ld	b,a
	ld	c,a
	dec	a
	ld	(qflag),a	; reset quote flag
	ld	a,(hl)		; get max byte count
	inc	hl		; point to current byte count
	ld	(clsave),hl	; save pointer
	ld	(hl),0		; be sure command line is empty
	inc	hl		; point to first char of command line
	ld	(clen),a	; store max command line length
;
; this is the main loop
;
getnxt:	call	getch		; get a character
	call	getcmd		; check if command, return zero if not
	jr	z,ascii		; no command, enter it into cmd line
	push	de		; jump to de
	ret
;
; enter A into command line, echo character
;
ascii:	push	af
	ld	a,(clen)	; get max line length
	cp	b		; reached?
	jr	nz,nolov	;   nope
	pop	af
	call	error		;      else beep
	jr	getnxt		;         and try again
;
nolov:	ld	a,c		; see if at end of line
	cp	b
	jr	nz,insert	; inside line, so do insert mode
	pop	af
	ld	(hl),a
	inc	hl		; increment
	inc	b		;    pointers
	inc	c
	call	outch
	jr	getnxt		; go get another
;
; INSERT - insert a character, move all others right
;
insert:	ld	a,(clen)	; get allowed length
	cp	b
	jr	nz,insne
	call	error		; complain
	pop	af
	jr	getnxt		;    and exit if too long
;
insne:	ld	a,b		; get total chars in line
	sub	c		; subtract cursor position
	push	bc		; save pointers
	ld	c,a		; set counter
	ld	b,0		; bc=number of bytes right of cursor
	add	hl,bc		; point to end of line
	ld	d,h		; duplicate in de
	ld	e,l
	inc	de		; de points past line
	inc	bc
	inc	bc
	lddr			; move line up one char
	inc	hl
	pop	bc		; get counters back
	inc	b		; increment cursor and count
	inc	c
	pop	af		; now get character
	inc	hl		; increment pointer
	ld	(hl),a		; save it in command line
	push	hl		; save pointer
	push	bc		; save counters
	ld	a,b
	sub	c		; count again
	inc	a
	ld	b,a
	ld	c,a
	ld	a,(hl)		; compensate if ctl char inserted
	cp	' '
	jr	nc,insl
	dec	c
insl:	ld	a,(hl)
	cp	' '
	jr	nc,insnc
	inc	c
insnc:	call	outch		; disply the char
	inc	hl
	djnz	insl
;
insd:	ld	b,c
	dec	b
insdl:	call	bkspc
	djnz	insdl
;
inse:	pop	bc
	pop	hl
	inc	hl
	jp	getnxt
;
; OUTCH - output a character, display control char if necessary
;
outch:	cp	' '		; check if ctl character
	jr	nc,noctl	; skip if not
	push	af		; save char
	ld	a,litc		; prefix char
	call	putch		; display it
	pop	af		; get char back
	add	a,40h		; make it alpha
noctl:	jp	putch		; display char
;
;
; COMMAND PROCESSOR ROUTINES
;
; CR typed -- exit back to turbodos
;
exit:	ld	a,c		; see if we're at end of line
	cp	b		; if so continue
	jr	z,cntxit
	inc	hl		; else up pointers
	inc	c
	jr	exit
;
cntxit:	ld	(hl),0		; terminate command line
	push	bc
	ld	a,cr
	call	echo		; echo the RETURN
	pop	bc
	ld	hl,(clsave)	; get command line pointer
	ld	(hl),b		; save actual byte count
	push	hl		; save
	push	bc		;    registers
	ld	c,b		; set up count
	ld	b,0
	inc	c
	ld	de,oldlin	; point to previous line buffer
	ldir			; store current line there
	pop	bc		; get regs back
	pop	hl
	ld	a,b		; byte count in A
	ret
;
;
; CBEGLI - go to beginning of line
;
cbegli:	inc	c		; set up cursor pointer
cbegl1:	dec	c		; decrement cursor ptr
	jr	z,..gn1		;    until zero
	call	bkspc
	dec	hl
	ld	a,(hl)
	cp	' '
	call	c,bkspc
	jr	cbegl1
;
; CENDLI - go to end of line
;
cendli:	ld	a,c		; get cursor
	cp	b		; if cursor at end of line
	jr	z,..gn1		;    then get next char
	inc	c		; point to next char
	ld	a,(hl)
	inc	hl
	cp	' '
	ld	a,(forspc)
	call	c,putch
	call	putch
	jr	cendli		; and so on
;
; CBACKC - back 1 char
;
cbackc:	ld	a,c		; get count
	or	a		; if at left
	jr	z,..gn1		;    do nothing
	call	bkspc		; else backspace
	dec	c		; decrement counter
	dec	hl		;    pointer
	ld	a,(hl)
	cp	' '
	call	c,bkspc
	jr	..gn1
;
; CFORWC - forward 1 char
;
cforwc:	ld	a,c		; get count
	cp	b		; if at end
	jr	z,..gn1		;    do nothing
	inc	c		; increment counter
	ld	a,(hl)
	cp	' '
	ld	a,(forspc)
	call	c,putch
	call	putch		; move cursor
	inc	hl		;    pointer
..gn1:	jp	getnxt
;
; CQUOTE - do next char literally
;
cquote:	xor	a
	ld	(qflag),a
	jr	..gn1
;
; CRECAL - recall previous command line
;
crecal:	inc	b		; test if b=0
	dec	b
	jr	nz,..gn1	; count must be 0
	ld	a,(oldlin)	; anything in?
	or	a
	jr	z,..gn1		;    no, empty
	push	hl
	ld	de,oldlin	; get old line
	ex	de,hl		; set up for move
	dec	de
	ld	c,(hl)
	ld	b,0
	push	bc
	inc	c
	ldir
	pop	bc
	ld	b,c		; get count into b
	pop	hl		; get pointer
dslp:	ld	a,(hl)		; get byte
	call	outch		; display it with ctl char expansion
	inc	hl		; point to next
	djnz	dslp		; until all done
	ld	b,c		; count = cursor
	jr	..gn1		; exit
;
; CDELBA - delete previous character, move following chars to left
;
cdelba:	ld	a,c		; see if at start of line
	or	a
	jr	z,..gn2
	ld	de,0
	call	dellft		; delete char to the left
	ld	a,b
	cp	c
	jr	z,..gn2
	jr	movdel		; move after delete
;
; CDELFO - delete following character, move remainder left
;
cdelfo:	ld	a,c		; if cursor at right end
	cp	b
	jr	z,..gn2		;    then do nothing
	ld	de,0
	dec	b
movdel:	ld	a,b		; see if end of line
	push	bc		; save counters
	push	hl		; save line ptr
	sub	c		; a=number of chars
	push	af
	inc	a
	ld	c,a
	ld	b,0
	push	de		; save control char count
	ld	d,h
	ld	e,l
	inc	hl
	ldir
	pop	de		; get control char count (0/1)
	pop	af		; get byte count
	pop	hl		; get line pointer
	push	hl		; save it again
	ld	b,a
	or	a
	jr	nz,mvdell
	ld	a,' '
	call	putch
	call	putch
	call	bkspc
	call	bkspc
	jr	mvdelq
;
mvdell:	ld	a,(hl)
	cp	' '
	jr	nc,mvd1
	inc	e
mvd1:	call	outch
	inc	e		; increment char count
	inc	hl
	djnz	mvdell
	ld	a,' '
	call	putch		; wipe out last char
	call	putch
	call	bkspc
mvdel1:	ld	b,e
	call	bkspc
mvdel2:	call	bkspc
	djnz	mvdel2
mvdelq:	pop	hl
	pop	bc
..gn2:	jp	getnxt
;
; CKILFO - kill all chars forward
;
ckilfo:	ld	a,b
	cp	c
	jr	z,..gn2		; nothing to kill
	sub	c		; get byte count
	ld	b,c
	push	bc		; save count, cursor
	ld	(killen),a	; store kill buffer length
	ld	c,a
	ld	b,0
	push	bc
	push	hl		; save line pointer
	ld	de,kilbuf
	ldir			; move into kill buffer
	pop	hl
	pop	bc
	push	hl
	ld	b,c
ckilf1:	ld	a,(hl)
	inc	hl
	cp	' '
	ld	a,' '
	jr	nc,ckilf2
	call	putch
	inc	c
ckilf2:	call	putch
	djnz	ckilf1
	ld	b,c
ckilf3:	call	bkspc
	djnz	ckilf3
	pop	hl
	pop	bc
	ld	(hl),0
	jr	..gn2
;
; CKILBA - kill all chars backward
;
ckilba:	ld	a,c
	or	a
	jr	z,..gn2		; nothing to kill
	push	bc		; save counters
	push	hl		; save pointer
	ld	de,(clsave)	; get pointer to start
	inc	de
	or	a
	sbc	hl,de		; hl is now length
	ld	b,h
	ld	c,l
	ld	hl,killen	; point to kill buffer
	ld	(hl),c
	inc	hl
	ex	de,hl
	ldir			; move stuff into kill buffer
	pop	hl		; get old ptr
	pop	bc
	push	bc
	push	hl
ckilb1:	dec	hl
	ld	a,(hl)
	cp	' '
	ld	a,8
	call	c,putch
	call	putch
	dec	c
	jr	nz,ckilb1
	ld	e,0
ckilb2:	ld	a,(hl)
	inc	hl
	cp	' '
	ld	a,' '
	jr	nc,ckilb3
	call	putch
	inc	e
ckilb3:	call	putch
	inc	e
	djnz	ckilb2
	ld	b,e
ckilb4:	call	bkspc
	djnz	ckilb4
	pop	hl		; get line ptr
	pop	bc		; get counters
	ld	a,b
	sub	c		; a now has byte count
	ld	c,a
	ld	b,0
	push	bc
	inc	bc
	ld	de,(clsave)
	inc	de
	push	de
	ldir
	pop	hl
	pop	bc
	ld	b,c
	ld	c,0
	ld	a,b
	or	a
	jr	z,..gn3
	push	hl
	push	bc
	ld	e,0
ckilb5:	ld	a,(hl)
	cp	' '
	jr	nc,ckilb6
	inc	e
	push	af
	ld	a,litc
	call	putch
	pop	af
	add	a,40h
ckilb6:	call	putch
	inc	e
	inc	hl
	djnz	ckilb5
	ld	b,e
ckilb7:	call	bkspc
	djnz	ckilb7
	pop	bc
	pop	hl
..gn3:	jp	getnxt
;
; CYANK - yank kill buffer to current cursor
;
cyank:	ld	a,(killen)	; get kill buffer length
	or	a
	jr	z,..gn3		; kill buffer empty
	add	a,b		; check total size
	jr	c,er1		; gee, much too much (>256)!
	ld	e,a
	ld	a,(clen)
	cp	e
	jr	nc,yntl		; go ahead if not too long
er1:	call	error		; too long
	jr	..gn3
;
yntl:	xor	a
	ld	(insy),a
	ld	a,b
	cp	c		; check if cursor at end of line
	ld	a,(killen)	; get length of kill buffer
	jr	z,nomove	; skip if at end of line
	push	bc		; save count
	push	hl		; save pointer
	ld	e,a
	ld	d,0		; de = number of bytes to be freed
	ld	a,b
	sub	c		; a has bytes to be moved
	ld	c,a
	ld	b,0		; bc has bytes tbm
	add	hl,bc		; point to end of line
	push	hl
	add	hl,de		; point to new end of line
	ex	de,hl
	pop	hl
	inc	bc
	inc	bc
	lddr
	pop	hl
	pop	bc
	ld	a,0ffh
	ld	(insy),a
	ld	a,(killen)	; get length of kill buffer
nomove:	push	bc
	push	hl
	ex	de,hl		; destination to de
	ld	hl,kilbuf
	ld	c,a
	ld	b,0
	push	bc
	ldir
	pop	de
	pop	hl
	pop	bc
ydslp:	ld	a,(hl)
	call	outch
	inc	b
	inc	c
	inc	hl
	dec	e
	jr	nz,ydslp
	ld	a,(insy)
	inc	a
	jr	nz,..gn3
;
; now redisplay remainder of line, then step back
;
	push	hl		; save line ptr
	push	bc
	ld	e,0		; character count
ytl1:	ld	a,c
	cp	b
	jr	z,ytl25
	inc	c
	ld	a,(hl)
	cp	' '
	jr	nc,ytl2
	inc	e
ytl2:	inc	e
	call	outch
	inc	hl
	jr	ytl1
;
ytl25:	pop	bc
ytl3:	call	bkspc
	dec	e
	jr	nz,ytl3
	pop	hl
	jr	..gn3
;
; abort line
;
cabort:	ld	bc,0
	ld	hl,(clsave)
	inc	hl
	jp	exit
;
	END
ointer
	ld	e,a
	ld	d,0		; de = number of bytes to be freed
	ld	a,b
	sub	c		; a has byt